package com.tumblr.logico2012.screens;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Vector;

import processing.core.PApplet;
import processing.core.PImage;
import processing.core.PVector;
import SimpleOpenNI.IntVector;
import SimpleOpenNI.SimpleOpenNI;

import com.tumblr.logico2012.IApplet;
import com.tumblr.logico2012.gestures.GestureManager;
import com.tumblr.logico2012.gestures.IControlPad;
import com.tumblr.logico2012.gestures.IGesture;
import com.tumblr.logico2012.gestures.ISwipeGesture;

import static com.tumblr.logico2012.AppletLogico.DISABLE_CAMERA;

public abstract class ILabyrinthScreen implements IScreen {
	public static final char WALL = '#';
	public static final char FREE = ' ';
	public static final char PLAYER1 = '1';
	public static final char PLAYER2 = '2';
	public static final char TARGET = '!';
	public final static float DELAY = 2f;
	
	protected char[][] m_grid; // grid
	protected int m_gridWidth;
	protected int m_gridHeight; 
	protected PVector m_player1; // pos of 1st player
	protected PVector m_player2; // pos of 2nd playxer
	protected PApplet p;
	protected PImage m_player1Icon;
	protected PImage m_player2Icon;
	protected SimpleOpenNI m_context;
	protected int m_framecounter;
	protected char m_winner;
	protected PopupScreen m_startPopup;
	protected PopupScreen m_winnerPopup;
	protected IApplet m_app;
	protected int m_level;
	
	// list of grids
	public final static String[] LABYRINTH = new String[] {
		"labyrinth01.txt",
		"labyrinth02.txt"
	};

	public ILabyrinthScreen(IApplet app, PApplet processor, SimpleOpenNI context, GestureManager manager, int level) {
		m_level = level;
		p = processor;
		m_app = app;
		m_winner = FREE;
		m_context = context;
		loadLabyrinthFile("data/"+LABYRINTH[level - 1]);
		
		m_player1Icon = p.loadImage("player1.jpg");
		m_player2Icon = p.loadImage("player2.jpg");

		m_framecounter = 0;
		
		m_app.addPopup(m_startPopup = new PopupScreen(p, 1, "Use your feet to move your player!"));
		m_startPopup.setActive();

		manager.setGestureInstruction("swipeLeftOut", "Back");
	}

	/**
	 * Load a grid from a text file.
	 * @param file Labyrinth file
	 */
	public void loadLabyrinthFile(String file) {
		Vector<String> filecontent = new Vector<String>();
		int i, j;
		
		try{
			FileInputStream fstream = new FileInputStream(file);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			while ((strLine = br.readLine()) != null) {
				filecontent.add(strLine);
			}
			in.close();
		} catch (Exception e){ // Catch exception if any
			  System.err.println("Error: " + e.getMessage());
		}
		
		m_gridHeight = filecontent.size();
		for(i = 0, m_gridWidth = 0; i < m_gridHeight; i++)
			m_gridWidth = Math.max(m_gridWidth, filecontent.get(i).length());
		m_grid = new char[m_gridWidth][m_gridHeight];
		
		for(j = 0; j < m_gridHeight; j++) {
			for(i = 0; i < m_gridWidth; i++) {
				m_grid[i][j] = i < filecontent.get(j).length() ? filecontent.get(j).charAt(i) : FREE;
				if(m_grid[i][j] == PLAYER1) {
					m_player1 = new PVector(i, j, 0);
				} else if(m_grid[i][j] == PLAYER2) {
					m_player2 = new PVector(i, j, 0);
				}
			}
		}
	}
	
	@Override
	public void draw() {
		if(m_winner != FREE) {
			if(!m_winnerPopup.isActive()) {
				leave();
				return;
			}
		}
		m_framecounter ++;
		p.background(255, 255, 255);
		int uw = p.width / m_gridWidth;
		int uh = p.height / m_gridHeight;

		int x, y;
		for(x = 0; x < m_gridWidth; x++) {
			for(y = 0; y < m_gridHeight; y++) {
				switch(m_grid[x][y]) {
				case WALL:
					p.fill(0x66, 0x7C, 0x26);
					p.noStroke();
					p.rect(x * uw, y * uh, uw, uh);
					break;
				case FREE:
					break;
				case PLAYER1:
					p.noStroke(); p.noFill();
					p.image(m_player1Icon, x * uw, y * uh, uw, uh);
					break;
				case PLAYER2:
					p.noStroke(); p.noFill();
					p.image(m_player2Icon, x * uw, y * uh, uw, uh);
					break;
				case TARGET:
					p.fill(0xc7, 0xa3, 0x17);
					p.noStroke();
					p.rect(x * uw, y * uh, uw, uh);
					break;
				}
			}
		}
	}

	@Override
	public void keyPressed() {
		if(DISABLE_CAMERA) {
			switch(p.key) {
			case PApplet.ESC:
				p.key = 0;
				leave();
				break;
			case 'w': case 'W':
				moveUp(PLAYER1);
				break;
			case 'a': case 'A':
				moveLeft(PLAYER1);
				break;
			case 's': case 'S':
				moveDown(PLAYER1);
				break;
			case 'd': case 'D':
				moveRight(PLAYER1);
				break;
			case PApplet.CODED:
				switch(p.keyCode) {
				case PApplet.UP:
					moveUp(PLAYER2);
					break;
				case PApplet.DOWN:
					moveDown(PLAYER2);
					break;
				case PApplet.LEFT:
					moveLeft(PLAYER2);
					break;
				case PApplet.RIGHT:
					moveRight(PLAYER2);
					break;
				}
				break;
			}
		}
	}
	
	public boolean moveUp(char player) {
		return move(player, 0, -1);
	}
	
	public boolean moveDown(char player) {
		return move(player, 0, 1);
	}
	
	public boolean moveLeft(char player) {
		return move(player, -1, 0);
	}
	
	public boolean moveRight(char player) {
		return move(player, 1, 0);
	}
	
	protected boolean move(char player, int xmove, int ymove) {
		if(m_winner != FREE) return false; 
		if(m_startPopup != null) if(m_startPopup.isActive()) return false;
		
		boolean hasReachedTarget = false;
		int x = player == PLAYER1 ? (int)m_player1.x : (int)m_player2.x;
		int y = player == PLAYER1 ? (int)m_player1.y : (int)m_player2.y;
		
		if(xmove > 0 && x == m_gridWidth - 1) return false; // cannot move right
		if(xmove < 0 && x == 0) return false; // cannot move left
		if(ymove > 0 && y == m_gridWidth - 1) return false; // cannot move down
		if(ymove < 0 && y == 0) return false; // cannot move up
		
		if(m_grid[x + xmove][y + ymove] == FREE || m_grid[x + xmove][y + ymove] == TARGET) {
			hasReachedTarget = m_grid[x + xmove][y + ymove] == TARGET;
			m_grid[x + xmove][y + ymove] = player;
			m_grid[x][y] = FREE;
			
			if(player == PLAYER1) m_player1 = new PVector(x+xmove, y+ymove, 0);
			else m_player2 = new PVector(x+xmove, y+ymove, 0);
			
			if(hasReachedTarget) {
				m_winner = player;
				m_app.addPopup(m_winnerPopup = new PopupScreen(p, 1, "Player "+player+" has won!"));
				m_winnerPopup.setActive();
			}
			return true;
		} else return false; // cannot move right
	}
	
	@Override
	public void registerUser(GestureManager manager, int userId) {
	    manager.addGesture(new SwipeGesture(userId, IGesture.RIGHT, IGesture.RIGHT));
//	    manager.addGesture(new SwipeGesture(userId, IGesture.LEFT, IGesture.RIGHT));
//	    manager.addGesture(new SwipeGesture(userId, IGesture.RIGHT, IGesture.LEFT));
	    manager.addGesture(new SwipeGesture(userId, IGesture.LEFT, IGesture.LEFT));	
		manager.addGesture(new ControlPad(userId, manager));		
	}

	@Override
	public String returnValue() {
		return new String(m_winner+"");
	}

	class ControlPad extends IControlPad {
		
		public ControlPad(int userId, GestureManager manager) {
			super(ILabyrinthScreen.this.p, userId, ILabyrinthScreen.this.m_context, "controlpad"+userId, false);
			manager.addIcon("controlpad"+userId, "controls.jpg");
			manager.setGestureInstruction("controlpad"+m_userId, "Move");
		}

		@Override
		public void gestureAborted(GestureManager manager) {
		}

		public char getPlayer() {
			char player = FREE;
			IntVector users = new IntVector();
			m_context.getUsers(users);
			if(users.size() >= 1) {
				if(users.get(0) == m_userId) {
					player = PLAYER1;
				}
			}
			if(users.size() >= 2) {
				if(users.get(1) == m_userId) {
					player = PLAYER2;
				}
			}
			return player;
		}
		
		@Override
		public void gestureEnded(GestureManager manager) {
			if(getPlayer() != FREE) {
				manager.addIcon("controlpad"+m_userId, "controls.jpg");
			}
		}

		@Override
		public void gestureStarted(GestureManager manager) {
			char player = getPlayer();
			if(player != FREE) {
				switch(m_direction) {
				case IGesture.UP:
					manager.addIcon("controlpad"+m_userId, "control_up.jpg");
					moveUp(player);
					break;		
				case IGesture.DOWN:
					manager.addIcon("controlpad"+m_userId, "control_down.jpg");
					moveDown(player);
					break;		
				case IGesture.LEFT:
					manager.addIcon("controlpad"+m_userId, "control_left.jpg");
					moveLeft(player);
					break;		
				case IGesture.RIGHT:
					manager.addIcon("controlpad"+m_userId, "control_right.jpg");
					moveRight(player);
					break;		
				}
			}
		}
	}

	class SwipeGesture extends ISwipeGesture {
		public SwipeGesture(int userId, int hand, int direction) {
			super(userId, hand, direction, ILabyrinthScreen.this.p, direction == IGesture.LEFT ? "swipeLeftOut" : "swipeRightOut");
		}

		@Override
		public void gestureAborted(GestureManager manager) {
			PApplet.println("swipe aborted!");
		}

		@Override
		public void gestureEnded(GestureManager manager) {
			PApplet.println("swipe ended!");
			if(m_framecounter > p.frameRate*DELAY) // small delay before being able to leave,
				// to avoid false positives when the user takes back his/her arm.
				leave();
		}

		@Override
		public void gestureStarted(GestureManager manager) {
			PApplet.println("swipe started!");
		}
	}	
}
